package com.oa.overtime.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.NativeTaskQuery;
import org.activiti.engine.task.Task;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.oa.organization.dao.OrganizationDao;
import com.oa.overtime.dao.IOvertimeDao;
import com.oa.overtime.dto.OvertimeDTO;
import com.oa.overtime.entity.Overtime;
import com.oa.system.dao.UserInfoDao;
import com.oa.util.SessionUtil;

@Service
@Transactional
public class OvertimeService implements IOvertimeService {
	/**
	 * 系统服务
	 */
	@Resource
	private IOvertimeDao overtimeDao;
	@Resource
	private UserInfoDao userInfoDao;
	@Resource
	private OrganizationDao organizationDao;

	/**
	 * 工作流服务
	 */
	@Resource
	private IdentityService identityService;
	@Resource
	private RuntimeService runtimeService;
	@Resource
	private TaskService taskService;
	@Resource
	private RepositoryService repositoryService;
	@Resource
	private ManagementService managementService;
	@Resource
	private HistoryService historyService;

	@Resource
	private HttpSession session;

	/**
	 * -------------------------------------系统业务----------------------------------------------
	 */
	@Override
	public void save(OvertimeDTO overtimeDTO) {
		Overtime overtime = new Overtime();
		dtoToEntity(overtimeDTO, overtime);

		overtimeDao.save(overtime);
	}

	@Override
	public void delete(Long id) {
		overtimeDao.delete(id);
	}

	@Override
	@Transactional(readOnly = true)
	public Overtime findOne(Long id) {
		return overtimeDao.findOne(id);
	}

	@Override
	@Transactional(readOnly = true)
	public Page<OvertimeDTO> findOvertimeForm(String name, Pageable pageable) {
		Page<Overtime> overtimePage = overtimeDao.findOvertimeForm(name, pageable);
		List<OvertimeDTO> dtoList = new ArrayList<>();
		for (Overtime o : overtimePage) {
			OvertimeDTO dto = new OvertimeDTO();
			entityToDto(o, dto);
			dtoList.add(dto);
		}
		return new PageImpl<>(dtoList);
	}

	@Override
	public Page<OvertimeDTO> findAll(Pageable pageable) {
		Page<Overtime> overtimePage = overtimeDao.findAll(pageable);
		List<OvertimeDTO> dtoList = new ArrayList<>();
		for (Overtime o : overtimePage) {
			OvertimeDTO dto = new OvertimeDTO();
			entityToDto(o, dto);
			dtoList.add(dto);
		}
		return new PageImpl<>(dtoList);
	}

	@Override
	public Page<OvertimeDTO> findOvertimeForm(Specification<Overtime> specification, Pageable pageable) {
		Page<Overtime> overtimePage = overtimeDao.findAll(specification, pageable);
		List<OvertimeDTO> dtoList = new ArrayList<>();
		for (Overtime o : overtimePage) {
			OvertimeDTO dto = new OvertimeDTO();
			entityToDto(o, dto);
			dtoList.add(dto);
		}
		return new PageImpl<>(dtoList);
	}

	// 实体转换DTO方法
	public void entityToDto(Overtime entity, OvertimeDTO dto) {
		BeanUtils.copyProperties(entity, dto);
		dto.setOvertimeDate(new SimpleDateFormat("yyyy/MM/dd").format(entity.getBeginDate()));
		dto.setBeginTime(new SimpleDateFormat("HH:mm").format(entity.getBeginDate()));
		dto.setEndTime(new SimpleDateFormat("HH:mm").format(entity.getEndDate()));
		if (entity.getRealBeginDate() != null && entity.getRealEndDate() != null) {
			dto.setRealOvertimeDate(new SimpleDateFormat("yyyy/MM/dd").format(entity.getRealBeginDate()));
			dto.setRealBeginTime(new SimpleDateFormat("HH:mm").format(entity.getRealBeginDate()));
			dto.setRealEndTime(new SimpleDateFormat("HH:mm").format(entity.getRealEndDate()));
		}

		// 获取流程任务中的代理人
		Task task = taskService.createTaskQuery().processInstanceId(entity.getProcessInstanceId()).singleResult();
		if (task != null) {
			dto.setAssignee(task.getAssignee());
			dto.setTaskId(task.getId());
			dto.setTaskDefinitionKey(task.getTaskDefinitionKey());
			dto.setTaskName(task.getName());
		}

	}

	// DTO转换实体方法
	public void dtoToEntity(OvertimeDTO dto, Overtime entity) {
		BeanUtils.copyProperties(dto, entity);

		if (dto.getOvertimeDate() != null && dto.getBeginTime() != null && dto.getEndTime() != null) {
			entity.setBeginDate(new Date(dto.getOvertimeDate() + " " + dto.getBeginTime()));
			entity.setEndDate(new Date(dto.getOvertimeDate() + " " + dto.getEndTime()));

			Float total = (float) ((entity.getEndDate().getTime() - entity.getBeginDate().getTime())
					/ (1000 * 60 * 60.0));
			entity.setTotalTime(total);
		}

		if (dto.getRealOvertimeDate() != null && dto.getRealBeginTime() != null && dto.getRealEndTime() != null) {
			entity.setRealBeginDate(new Date(dto.getRealOvertimeDate() + " " + dto.getRealBeginTime()));
			entity.setRealEndDate(new Date(dto.getRealOvertimeDate() + " " + dto.getRealEndTime()));

			Float total = (float) ((entity.getRealEndDate().getTime() - entity.getRealBeginDate().getTime())
					/ (1000 * 60 * 60.0));
			entity.setRealTotalTime(total);
		}

		if (entity.getProcessInstanceId() == null) {
			entity.setProcessInstanceId("");
		}
		entity.toString();
	}

	@Override
	public OvertimeDTO findOneDTO(Long id) {
		OvertimeDTO dto = new OvertimeDTO();
		Overtime overtime = overtimeDao.findOne(id);
		entityToDto(overtime, dto);
		return dto;
	}

	@Override
	public Page<OvertimeDTO> findLateForm() {
		Page<Overtime> oPage = overtimeDao.findOvertimeForm(SessionUtil.getName(session), new PageRequest(0, 5));

		List<OvertimeDTO> dtoList = new ArrayList<>();
		int i = 0;
		for (Overtime o : oPage) {
			OvertimeDTO dto = new OvertimeDTO();
			entityToDto(o, dto);
			dtoList.add(dto);
			i++;
			if (i == 5) {
				break;
			}
		}
		return new PageImpl<>(dtoList);
	}

	@Override
	public HSSFWorkbook export(Specification<Overtime> specification) {
		String[] excelHeader = { "姓名", "所属部门", "申请时间", "实际开始时间", "实际结束时间", "实际加班时长" };
		SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Page<Overtime> page = overtimeDao.findAll(specification, new PageRequest(0, 10, Direction.DESC, "name"));
		List<Overtime> overtimeList = new ArrayList<>();

		for (Overtime o : page) {
			overtimeList.add(o);
		}

		HSSFWorkbook wb = new HSSFWorkbook();
		HSSFSheet sheet = wb.createSheet("加班");
		HSSFCellStyle style = wb.createCellStyle();
		HSSFCellStyle cellStyle = wb.createCellStyle();
		HSSFRow row = sheet.createRow(0);
		Font font = wb.createFont();
		Font cellFont = wb.createFont();
		font.setFontHeightInPoints((short) 14);
		cellFont.setFontHeightInPoints((short)12);
		style.setAlignment(HorizontalAlignment.CENTER);
		style.setFont(font);
		cellStyle.setAlignment(HorizontalAlignment.CENTER);
		cellStyle.setFont(cellFont);

		for (int i = 0; i < overtimeList.size(); i++) {
			HSSFRow row2 = sheet.createRow(i + 1);
			Overtime o = overtimeList.get(i);
			row2.createCell(0).setCellValue(o.getName());
			row2.createCell(1).setCellValue(o.getDepartment());
			row2.createCell(2).setCellValue(format.format(o.getApplicationTime()));
			row2.createCell(3).setCellValue(format.format(o.getRealBeginDate()));
			row2.createCell(4).setCellValue(format.format(o.getRealEndDate()));
			row2.createCell(5).setCellValue(o.getRealTotalTime());
		}

		for (int i = 0; i < excelHeader.length; i++) {
			HSSFCell cell = row.createCell(i);
			cell.setCellValue(excelHeader[i]);
			cell.setCellStyle(style);
			sheet.autoSizeColumn(i);
		}

		return wb;
	}

	/**
	 * ----------------------------------工作流业务--------------------------------------------------
	 */
	/**
	 * 启动一个部署好的加班流程
	 * 
	 * @param overtimeId
	 *            用于启动流程的加班申请表ID，不能为空
	 * @param variables
	 *            流程变量，需要有2个键值对："applyUserId":Long,"depManager"
	 * 
	 * @return 启动的流程实例
	 */
	public ProcessInstance starProcess(Long overtimeId, Map<String, Object> variables) {
		// 声明流程实例
		ProcessInstance processInstance = null;
		// 获取其创建的加班实例
		Overtime overtime = overtimeDao.findOne(overtimeId);
		if (overtime != null) {
			try {
				// 授权，设置启动流程的用户Id
				identityService.setAuthenticatedUserId(overtime.getUserId().toString());
				// 将加班表的Id作为流程业务的主键
				String businessKey = overtime.getId().toString();
				// 启动流程
				processInstance = runtimeService.startProcessInstanceByKey("overtime", businessKey, variables);
				Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();

				overtime.setProcessInstanceId(processInstance.getId());
				overtime.setApplicationTime(new Date());
				overtime.setState("审批中");

				overtimeDao.save(overtime);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return processInstance;
	}

	@Override
	public Page<OvertimeDTO> findTodoTask(Long userId, Pageable pageable) {
		List<OvertimeDTO> results = new ArrayList<>();
		// 根据当前用户Id查询待完成流程任务
		// 用于原生查询的sql语句
		String sql = "select * from " + managementService.getTableName(TaskEntity.class) + " T left join  "
				+ managementService.getTableName(IdentityLinkEntity.class)
				+ " I on I.TASK_ID_ = T.ID_ where T.TASK_DEF_KEY_ != 'hrFile' and (I.GROUP_ID_ = #{group} and T.ASSIGNEE_ is null or T.ASSIGNEE_ = #{assignee});";
		// 使用原生查询查找当前用户待完成的流程任务Id
		NativeTaskQuery taskQuery = taskService.createNativeTaskQuery().sql(sql)
				.parameter("group", userInfoDao.findOne(userId).getRole().getRole()).parameter("assignee", userId);
		List<Task> tasks = taskQuery.list();
		// 根据流程任务ID查询流程实例并与数据关联
		for (Task task : tasks) {
			String processInstanceId = task.getProcessInstanceId();
			ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
					.processInstanceId(processInstanceId).active().singleResult();
			String businessKey = processInstance.getBusinessKey();
			if (businessKey == null) {
				continue;
			}
			Overtime overtime = overtimeDao.findOne(new Long(businessKey));
			if (overtime != null) {
				OvertimeDTO overtimeDTO = new OvertimeDTO();
				entityToDto(overtime, overtimeDTO);

				// 将转换后的数据添加到结果集中
				results.add(overtimeDTO);
			}
		}

		return new PageImpl<>(results);
	}

	@Override
	public void claim(String taskId, Long userId) {
		taskService.claim(taskId, userId.toString());
	}

	@Override
	public void complete(OvertimeDTO dto, Map<String, Object> variables) {
		taskService.complete(dto.getTaskId(), variables);

		// 更新表单的数据
		Overtime overtime = overtimeDao.findOne(dto.getId());
		overtime.setState("审批中");
		if (dto.getRejectReason() != null) {
			overtime.setRejectReason(dto.getRejectReason());
			overtime.setState("申请被驳回");
		}

		Task task = taskService.createTaskQuery().processInstanceId(dto.getProcessInstanceId()).singleResult();
		if (task != null) {
			if ("hrFile".equals(task.getTaskDefinitionKey())) {
				overtime.setState("已通过审批");
			}
		} else {// task为空，即流程已结束或者未开始，设置表单中task相关数据为空
			if ("reApply".equals(dto.getTaskDefinitionKey())) {
				overtime.setState("已取消申请");
			}
		}

		overtimeDao.save(overtime);
	}

	@Override
	public boolean isProcessInstanceActive(String ProcessInstanceId) {
		List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
				.processInstanceId(ProcessInstanceId).active().list();
		if (processInstances.size() == 0) {
			return false;
		} else {
			return true;
		}
	}

	@Override
	public Page<OvertimeDTO> findToFileForm(Long userId, Pageable pageable) {
		List<OvertimeDTO> result = new ArrayList<>();
		// 定义sql语句
		String sql = "select * from " + managementService.getTableName(TaskEntity.class) + " T left join  "
				+ managementService.getTableName(IdentityLinkEntity.class)
				+ " I on I.TASK_ID_ = T.ID_ where T.TASK_DEF_KEY_ = 'hrFile' and I.GROUP_ID_ = #{group} ;";
		// 使用原生的查询方法查询task
		List<Task> tasks = taskService.createNativeTaskQuery().sql(sql)
				.parameter("group", userInfoDao.findOne(userId).getRole().getRole()).list();
		for (Task t : tasks) {
			String processInstanceId = t.getProcessInstanceId();
			ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
					.processInstanceId(processInstanceId).active().singleResult();
			String businessKey = processInstance.getBusinessKey();
			if (businessKey == null) {
				continue;
			}
			Overtime overtime = overtimeDao.findOne(new Long(businessKey));
			if (overtime != null) {
				OvertimeDTO dto = new OvertimeDTO();
				entityToDto(overtime, dto);

				result.add(dto);
			}

		}
		return new PageImpl<>(result);
	}

	@Override
	public void overtimeFile(OvertimeDTO dto) {
		Overtime overtime = overtimeDao.findOne(dto.getId());
		Overtime fOvertime = new Overtime();

		dtoToEntity(dto, fOvertime);
		overtime.setRealBeginDate(fOvertime.getRealBeginDate());
		overtime.setRealEndDate(fOvertime.getRealEndDate());
		overtime.setRealTotalTime(fOvertime.getRealTotalTime());
		overtime.setState("已完成");

		taskService.complete(dto.getTaskId());

		overtimeDao.save(overtime);
	}

	@Override
	public boolean isProcessInstanceExist(String ProcessInstanceId) {
		List<HistoricProcessInstance> processInstances = historyService.createHistoricProcessInstanceQuery()
				.processInstanceId(ProcessInstanceId).list();
		if (processInstances.size() != 0) {
			return true;
		}
		return false;
	}

	@Override
	public String getDeptManager(String detName) {
		return userInfoDao.findOne(organizationDao.findByName(detName).getLeadId()).getRole().getRole();
	}


}
